/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JLabel;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.patterns.Normalise;
import ro.dta.idbi.ui.sql.SqlGraph;
import ro.dta.idbi.ui.sql.SqlTableCell;
import uk.ac.ic.doc.automed.reps.Schema;

import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;

/**
 * UI for the normalisation pattern
 * 
 * @author Tudor Dobrila
 * 
 */
public class NormaliseDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = -3639126240305579327L;
	private static final Logger LOGGER = Logger
			.getLogger("com.dta.idbi.patterns.ui.NormaliseDialog");
	
	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		return objects.length == 1 && objects[0] instanceof ISqlTable;
	}
	
	private transient SqlTableCell rightTable;

	/**
	 * Normalisation pattern UI dialog
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param table
	 *            Table that is normalised
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public NormaliseDialog(final IMainFrame parentFrame, final Schema schema,
			final ISqlConstruct[] table, final JLabel beforeLabel, final JLabel afterLabel) {
		super(parentFrame, schema, table, beforeLabel, afterLabel);

		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given elements");
		}

		macroField.setText(Normalise.COMMAND + " (<<" + element[0].getValue()
				+ ">>, <<newTable>>, {}, {})");
	}

	/**
	 * Create the left panel, showing the original table, before normalisation
	 */
	protected mxGraphComponent createOriginalDiagram() {
		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			final SqlTableCell newTable = (SqlTableCell) element[0].clone();
			final mxGeometry newGeometry = new mxGeometry(5, 5,
					element[0].getGeometry().getWidth(), element[0].getGeometry().getHeight());
			newTable.setGeometry(newGeometry);
			newTable.layout(graph);
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

	/**
	 * Create the right panel cotaining the transformed table
	 */
	protected mxGraphComponent createTransformedDiagram() {
		final NormaliseGraph graph = new NormaliseGraph();
		graph.getModel().beginUpdate();

		SqlTableCell newTable = null;
		ISqlColumn addDetCell = null;
		ISqlColumn addDepCell = null;

		try {
			try {
				newTable = (SqlTableCell) element[0].clone();
				final mxGeometry newGeometry = new mxGeometry(5, 5, element[0].getGeometry()
						.getWidth(), element[0].getGeometry().getHeight());
				newTable.setGeometry(newGeometry);
				newTable.layout(graph);
			} catch (CloneNotSupportedException e) {
				LOGGER.log(Level.SEVERE, "Fatal exception", e);
			}

			rightTable = new SqlTableCell(null, null, null, "newtable", 220, 5, 150, 30, "NEWTABLE");
			addDetCell = rightTable.addColumn("drop determinant", "NEWCOLUMN");
			addDepCell = rightTable.addColumn("drop dependent", "NEWCOLUMN");
			rightTable.layout(graph);
		} finally {
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		final NormaliseGraphComp graphComponent = new NormaliseGraphComp(graph, newTable,
				rightTable, addDetCell, addDepCell);

		graphComponent.addNormaliseEventListener(new NormaliseListener() {

			@Override
			public void newTableChanged(final NormaliseEvent event) {
				updateMacro();
			}
		});

		graphComponent.addListener(mxEvent.LABEL_CHANGED, new mxIEventListener() {

			@Override
			public void invoke(final Object arg0, final mxEventObject arg1) {
				updateMacro();
			}
		});

		return graphComponent;
	}

	private void updateMacro() {
		final StringBuilder builder = new StringBuilder(Normalise.COMMAND).append(" (<<");
		builder.append(element[0].getValue()).append(">>, <<");
		builder.append(rightTable.getValue()).append(">>, ");

		int noDets = 0, noDeps = 0;
		final StringBuilder detSet = new StringBuilder();
		final StringBuilder depSet = new StringBuilder();
		for (ISqlColumn col : rightTable.getColumns()) {
			if (col.getStyle().equals("DETERMINANT")) {
				noDets++;
				detSet.append(col.getValue()).append(", ");
			}

			if (col.getStyle().equals("DEPENDENT")) {
				noDeps++;
				depSet.append(col.getValue()).append(", ");
			}
		}

		if (noDets > 0) {
			detSet.replace(detSet.length() - 2, detSet.length(), "");
			if (noDets > 1) {
				detSet.insert(0, '{');
				detSet.append('}');
			}
		} else {
			detSet.append("{}");
		}
		if (noDeps > 0) {
			depSet.replace(depSet.length() - 2, depSet.length(), "");
			if (noDeps > 1) {
				depSet.insert(0, '{');
				depSet.append('}');
			}
		} else {
			depSet.append("{}");
		}

		builder.append(detSet).append(", ");
		builder.append(depSet).append(")");

		macroField.setText(builder.toString());
	}
}
